package creation

import (
	"context"
	"fmt"
	"github.com/nuttech/bell"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	v1alpha12 "gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
	"gitverse.ru/synapse/kubelatte/pkg/kubeapi"
	"gitverse.ru/synapse/kubelatte/pkg/observability/logs"
	"gitverse.ru/synapse/kubelatte/pkg/operator"
	"gitverse.ru/synapse/kubelatte/pkg/operator/controllers/clientset"
	"gitverse.ru/synapse/kubelatte/pkg/operator/controllers/clientset/mocks"
	"gitverse.ru/synapse/kubelatte/pkg/storage"
	"gitverse.ru/synapse/kubelatte/pkg/util"
	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/rest/fake"
	"os"
	"testing"
	"time"
)

func TestMain(m *testing.M) {
	logs.Logger = zap.NewNop()
	exitVal := m.Run()
	os.Exit(exitVal)
}

func TestFactoryController_CheckIfProcessed(t *testing.T) {
	ti := v1alpha12.TriggerInstance{
		Status: v1alpha12.TriggerInstanceStatus{
			ResourceStatus: []v1alpha12.ResourceStatus{
				{
					Name:       "pod",
					Kind:       "Pod",
					ApiVersion: "v1",
					Message:    "REMOVED",
					Phase:      "",
					Status:     "REMOVED",
				},
			},
		},
	}
	ti2 := v1alpha12.TriggerInstance{
		Status: v1alpha12.TriggerInstanceStatus{
			ResourceStatus: []v1alpha12.ResourceStatus{
				{
					Name:       "pod",
					Kind:       "Pod",
					ApiVersion: "v1",
					Message:    "REMOVED",
					Phase:      "",
					Status:     "FAILED",
				},
			},
		},
	}
	f := FactoryController{}
	b1 := f.checkIfProcessed("pod", &ti2, true)
	assert.True(t, !b1)
	b2 := f.checkIfProcessed("pod", &ti, true)
	assert.True(t, b2)
	b3 := f.checkIfProcessed("pod", &ti, false)
	assert.True(t, b3)
}

func TestFactoryController_Make(t *testing.T) {
	logs.Logger = zap.NewNop()

	ti := v1alpha12.TriggerInstance{
		Status: v1alpha12.TriggerInstanceStatus{
			ResourceStatus: []v1alpha12.ResourceStatus{
				{
					Name:       "pod",
					Kind:       "Pod",
					ApiVersion: "v1",
					Message:    "REMOVED",
					Phase:      "",
					Status:     "REMOVED",
				},
			},
		},
	}
	ti2 := v1alpha12.TriggerInstance{
		ObjectMeta: v1.ObjectMeta{
			Name:      "test",
			Namespace: "test",
			Labels: map[string]string{
				"test": "test",
			},
			Annotations: map[string]string{
				"test": "test",
			},
		},
		Status: v1alpha12.TriggerInstanceStatus{},
	}

	templateClient := mocks.NewTemplateInterface(t)
	templateClient.On("List", mock.Anything, mock.Anything).Return(&v1alpha12.TemplateList{
		Items: []v1alpha12.Template{
			{
				ObjectMeta: v1.ObjectMeta{
					Name:      "template",
					Namespace: "test",
				},
				Spec: v1alpha12.TemplateSpec{
					Kind:       "Pod",
					ApiVersion: "v1",
					Data:       "metadata:\n name: pod\nspec:\n abc: test",
				},
			},
		},
	}, nil).Maybe()
	triggerClient := mocks.NewTriggerInterface(t)
	triggerClient.On("List", mock.Anything, mock.Anything).Return(&v1alpha12.TriggerList{
		Items: []v1alpha12.Trigger{
			{
				ObjectMeta: v1.ObjectMeta{
					Name:      "trigger",
					Namespace: "test",
				},
				Spec: v1alpha12.TriggerSpec{
					CreationConfigs: []v1alpha12.CreationConfig{
						{
							Name: "creation",
							LabelSelector: v1.LabelSelector{
								MatchExpressions: []v1.LabelSelectorRequirement{
									{
										Key:      "test",
										Operator: "In",
										Values:   []string{"test"},
									},
								},
							},
							AnnotationNamespace: "",
							AnnotationTrigger:   "",
							TemplateRefs:        []string{"test/template"},
						},
					},
				},
			},
		},
	}, nil).Maybe()
	scopeClient := mocks.NewScopeInterface(t)
	triggerInstanceClient := mocks.NewTriggerInstanceInterface(t)
	triggerInstanceClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&ti, nil)
	triggerInstanceClient.On("UpdateStatus", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)

	clt := clientset.V1Alpha1Client{
		TriggerClient:         triggerClient,
		TemplateClient:        templateClient,
		ScopeClient:           scopeClient,
		TriggerInstanceClient: triggerInstanceClient,
	}

	operator.Cl = &clt
	kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
		return &kubeapi.ResourceMeta{
			Name:         "Pod",
			Plural:       "pods",
			Path:         "api",
			Namespaced:   true,
			GroupVersion: "v1",
		}
	}

	var storageController = &storage.StorageController{}
	storageController.Start(false, false)
	storageController.UpdateScope(bell.Message{
		Event:     "",
		Timestamp: time.Now(),
		Value: v1alpha12.Scope{
			ObjectMeta: v1.ObjectMeta{
				Name: "TestScope",
			},
			Spec: v1alpha12.ScopeSpec{
				Type: "mutation",
				Items: []v1alpha12.Item{
					{
						Name: "TestScopeItem",
					},
				},
			},
		},
	})
	storageController.UpdateTrigger(bell.Message{
		Event:     "",
		Timestamp: time.Now(),
		Value: v1alpha12.Trigger{
			ObjectMeta: v1.ObjectMeta{
				Name:      "trigger",
				Namespace: "test",
			},
			Spec: v1alpha12.TriggerSpec{
				CreationConfigs: []v1alpha12.CreationConfig{
					{
						Name: "creation",
						LabelSelector: v1.LabelSelector{
							MatchExpressions: []v1.LabelSelectorRequirement{
								{
									Key:      "test",
									Operator: "In",
									Values:   []string{"test"},
								},
							},
						},
						AnnotationNamespace: "",
						AnnotationTrigger:   "",
						TemplateRefs:        []string{"test/template"},
					},
				},
			},
		},
	})
	storageController.UpdateTemplate(bell.Message{
		Event:     "",
		Timestamp: time.Now(),
		Value: v1alpha12.Template{
			ObjectMeta: v1.ObjectMeta{
				Name:      "template",
				Namespace: "test",
			},
			Spec: v1alpha12.TemplateSpec{
				Kind:       "Pod",
				ApiVersion: "v1",
				Data:       "metadata:\n name: pod\nspec:\n abc: test",
			},
		},
	})
	storage.Storage = storageController

	RC = &RenderController{}

	mes := bell.Message{
		Event:     "event",
		Timestamp: time.Time{},
		Value:     ti,
	}
	mes2 := bell.Message{
		Event:     "event",
		Timestamp: time.Time{},
		Value:     ti2,
	}
	sch := runtime.NewScheme()
	_ = corev1.SchemeBuilder.AddToScheme(sch)
	podJson := "{\"kind\":\"Pod\",\"apiVersion\":\"v1\",\"metadata\":{\"generateName\":\"keymaker-keymaker-769579fd65-\",\"creationTimestamp\":null,\"labels\":{\"app.kubernetes.io/instance\":\"keymaker\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"keymaker\",\"pod-template-hash\":\"769579fd65\"},\"annotations\":{\"artifact.spinnaker.io/location\":\"keymaker\",\"artifact.spinnaker.io/name\":\"keymaker-keymaker\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"moniker.spinnaker.io/application\":\"securitykaaskeymaker\",\"moniker.spinnaker.io/cluster\":\"deployment keymaker-keymaker\",\"keymaker.k8s-integration.sfdc.com/inject\":\"true\",\"madkub.k8s-integration.sfdc.com/inject\":\"true\",\"port/value\":\"1111\"},\"ownerReferences\":[{\"apiVersion\":\"apps/v1\",\"kind\":\"ReplicaSet\",\"name\":\"keymaker-keymaker-769579fd65\",\"uid\":\"dcdda283-a4fc-11e9-a276-42010a3e0104\",\"controller\":true,\"blockOwnerDeletion\":true}]},\"spec\":{\"volumes\":[{\"name\":\"kaas-keymaker-orlgsgcsl34imsg6-token-kn2t8\",\"secret\":{\"secretName\":\"kaas-keymaker-orlgsgcsl34imsg6-token-kn2t8\"}}],\"initContainers\":[{\"name\":\"ca-populator-original\",\"image\":\"/dva/pki_aws_bootstrap:6\",\"args\":[\"/bin/cp\",\"/etc/ssl/certs/cacerts.pem\",\"/etc/pki_service/ca/cacerts.pem\"],\"resources\":{},\"terminationMessagePath\":\"/dev/termination-log\",\"terminationMessagePolicy\":\"File\",\"imagePullPolicy\":\"IfNotPresent\"},{\"name\":\"madkub-init-original\",\"image\":\"/dva/pki_aws_bootstrap:6\",\"args\":[\"/bin/cp\",\"/etc/ssl/certs/cacerts.pem\",\"/etc/pki_service/ca/cacerts.pem\"],\"resources\":{},\"terminationMessagePath\":\"/dev/termination-log\",\"terminationMessagePolicy\":\"File\",\"imagePullPolicy\":\"IfNotPresent\"}],\"containers\":[{\"name\":\"keymaker-original\",\"image\":\"gcr.io/gsf-mgmt-devmvp-spinnaker/dva/keymaker-client:latest\",\"command\":[\"sleep\"],\"args\":[\"infinity\"],\"ports\":[{\"name\":\"http\",\"containerPort\":7020,\"protocol\":\"TCP\"}],\"resources\":{},\"volumeMounts\":[{\"name\":\"kaas-keymaker-orlgsgcsl34imsg6-token-kn2t8\",\"readOnly\":true,\"mountPath\":\"/var/run/secrets/kubernetes.io/serviceaccount\"}],\"terminationMessagePath\":\"/dev/termination-log\",\"terminationMessagePolicy\":\"File\",\"imagePullPolicy\":\"Always\"}],\"restartPolicy\":\"Always\",\"terminationGracePeriodSeconds\":30,\"dnsPolicy\":\"ClusterFirst\",\"serviceAccountName\":\"kaas-keymaker-orlgsgcsl34imsg6\",\"serviceAccount\":\"kaas-keymaker-orlgsgcsl34imsg6\",\"automountServiceAccountToken\":true,\"securityContext\":{},\"schedulerName\":\"default-scheduler\",\"tolerations\":[{\"key\":\"node.kubernetes.io/not-ready\",\"operator\":\"Exists\",\"effect\":\"NoExecute\",\"tolerationSeconds\":300},{\"key\":\"node.kubernetes.io/unreachable\",\"operator\":\"Exists\",\"effect\":\"NoExecute\",\"tolerationSeconds\":300}],\"priority\":0},\"status\":{}}"

	//kubeapi.GetResourceFromKubeApi = func(ctx context.Context, client rest.Interface, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string, getResult runtime.Object) error {
	//	return errors.NewNotFound(schema.GroupResource{}, "Pod test")
	//}

	kubeapi.CreateResourceInKubeApi = func(ctx context.Context, client rest.Interface, resourceMeta *kubeapi.ResourceMeta, jsBody []byte, namespaceName string) ([]byte, error, error) {
		return []byte(podJson), nil, nil
	}

	f := &FactoryController{
		Client: kubernetes.New(&fake.RESTClient{}),
	}

	t.Run("Create resource with removed status", func(t *testing.T) {
		f.make(mes)
	})
	t.Run("Create normal resource", func(t *testing.T) {
		f.make(mes2)
	})

	//kubeapi.getResourceFromKubeApi = func(ctx context.Context, client rest.Interface, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string, getResult runtime.Object) error {
	//	return fmt.Errorf("Test error")
	//}

	t.Run("Create resource with error on find", func(t *testing.T) {
		f.make(mes2)
	})

	kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
		return nil
	}

	t.Run("Create normal resource with no meta", func(t *testing.T) {
		f.make(mes2)
	})
}

func TestFactoryController_Start(t *testing.T) {
	kubeapi.GetClient = func() *kubernetes.Clientset {
		return nil
	}
	f := &FactoryController{}
	f.Start()
	f.Stop()
}

func TestFactoryController_GetResourceName(t *testing.T) {
	type fields struct {
		Client   *kubernetes.Clientset
		chObject chan bell.Message
		chStop   chan bool
	}
	type args struct {
		tiName       string
		templateName string
		kind         string
		render       util.RenderItem
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    string
		wantErr assert.ErrorAssertionFunc
	}{
		{
			name: "not_unique_name",
			args: args{
				tiName:       "ti",
				templateName: "tmpl",
				kind:         "pod",
				render:       util.RenderItem{Render: "{\"metadata\":{\"name\":\"name\"}}"},
			},
			want: "",
			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
				assert.False(t, err != nil)
				return err != nil
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			f := &FactoryController{}
			got, err := f.getResourceName(tt.args.tiName, tt.args.kind, tt.args.render)
			if !tt.wantErr(t, err, fmt.Sprintf("GetResourceName(%v, %v, %v, %v)", tt.args.tiName, tt.args.templateName, tt.args.kind, tt.args.render)) {
				return
			}
			assert.Equalf(t, tt.want, got, "GetResourceName(%v, %v, %v, %v)", tt.args.tiName, tt.args.templateName, tt.args.kind, tt.args.render)
		})
	}
}
